using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Sublimation
{
    public class GameMap : Microsoft.Xna.Framework.DrawableGameComponent
    {
        SpriteBatch spriteBatch;

        protected Texture2D backgroundSprite;
        public Texture2D BackgroundSprite
        {
            get { return this.backgroundSprite; }
        }
        protected Rectangle backgroundSourceRectangle;
        public Rectangle BackgroundSourceRectangle
        {
            get { return this.backgroundSourceRectangle; }
        }
        protected String mapName;
        public String MapName
        {
            get { return this.mapName; }
        }
        protected String displayName;
        public String DisplayName
        {
            get { return this.displayName; }
        }
        protected Rectangle worldBounds;
        public Rectangle WorldBounds
        {
            get { return this.worldBounds; }
        }
        protected int perimeter;
        public int Perimeter
        {
            get { return perimeter; }
        }

        protected List<Rectangle> physicalObjects;
        public List<Rectangle> PhysicalObjects
        {
            get { return physicalObjects; }
            set {  physicalObjects= value; }
        }

        protected WorldProperties worldProperties;
        
        public GameMap(WorldProperties worldProperties, String mapName)
            : base(worldProperties.Game)
        {
            this.mapName = mapName;
            // TODO: check if it's a valid map
            this.worldProperties = worldProperties;

            Game.Components.Add(this);

            // make sure it loads and draws
            DrawOrder = 0;
            UpdateOrder = 95;
            Visible = true;
            Enabled = true;

            //Initialize();
            LoadContent();
        }

        // decals
        protected Queue<Decal> decals;
        public const int MAX_DECALS = 400;
        protected Texture2D decalBlank;
        protected Texture2D decalBloodSplatter;
        protected Texture2D decalScorchMark;
        protected override void LoadContent()
        {
            // Setup a SpriteBatch for this set of rendering
            spriteBatch = worldProperties.SpriteBatch;

            // Load the map
            this.backgroundSprite = Game.Content.Load<Texture2D>(mapName + "\\Sprites\\map");
            this.backgroundSourceRectangle = new Rectangle(0, 0, 1024, 768);
            this.displayName = this.mapName; //TODO: change this to read a property

            // this may change when porting to different places, though maybe the map should be the same
            this.worldBounds = new Rectangle(0, 0, 1024, 768);
            this.perimeter = worldBounds.Width * 2 + worldBounds.Height * 2;

            // preload decals for the game
            decalBlank = Game.Content.Load<Texture2D>("Decals\\Blank");
            decalBloodSplatter = Game.Content.Load<Texture2D>("Decals\\BloodSplatter");
            decalScorchMark = Game.Content.Load<Texture2D>("Decals\\ScorchMark");
            decals = new Queue<Decal>();
            for (int i = 0; i < MAX_DECALS; i++)
            {
                decals.Enqueue(new Decal(decalBlank));
            }

            base.LoadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            base.Draw(gameTime);

            spriteBatch.Begin(SpriteBlendMode.None);
            // draw the map
            spriteBatch.Draw(
                BackgroundSprite,
                Vector2.Zero,
                BackgroundSourceRectangle,
                Color.White);
            spriteBatch.End();

            spriteBatch.Begin(SpriteBlendMode.AlphaBlend);
            // draw decals
            foreach (Decal decal in decals)
            {
                if (decal.isAlive())
                {
                    spriteBatch.Draw(decal.Sprite, decal.Position, null, decal.Color, decal.Rotation, decal.Origin, decal.Scale, SpriteEffects.None, 1.0f);
                }
            }
            spriteBatch.End();
        }

        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);

            // update decals
            foreach (Decal decal in decals)
            {
                if (decal.isAlive())
                {
                    decal.ExpiryTimeRemainingMillis -= gameTime.ElapsedGameTime.TotalMilliseconds;
                    decal.SetAlpha((byte)(decal.Color.A - decal.Color.A * 0.20f * (float)(gameTime.ElapsedGameTime.TotalMilliseconds / decal.MaxExpiryTimeMillis)));
                }
            }
        }

        public const double BLOOD_SPLATTER_EXPIRY_TIME_MILLIS = 2600;
        public void DrawBloodSplatter(float damage, Unit target)
        {
            Decal decal = decals.Dequeue();
            decal.Reset(decalBloodSplatter);
            decal.Position = target.Position;
            decal.Scale = (0.75f * target.CollisionRadius + 0.25f * target.CollisionRadius * Math.Max((damage / target.MaxLife), 1.0f)) / decalBloodSplatter.Width;
            //decal.Origin *= decal.Scale;
            decal.Rotation = (float)worldProperties.Random.NextDouble() * MathHelper.TwoPi;
            decal.Color = new Color(Color.White, Math.Min( 210*(damage/100),190));
            decal.MaxExpiryTimeMillis = BLOOD_SPLATTER_EXPIRY_TIME_MILLIS;
            decals.Enqueue(decal);
        }

        public const double SCORCH_MARK_EXPIRY_TIME_MILLIS = 1400;
        public void DrawScorchMark(Vector2 position, float scale)
        {
            Decal decal = decals.Dequeue();
            decal.Reset(decalScorchMark);
            decal.Position = position;
            decal.Scale = scale / decalScorchMark.Width;
            //decal.Origin *= decal.Scale;
            decal.Rotation = (float)worldProperties.Random.NextDouble() * MathHelper.TwoPi;
            decal.Color = new Color(Color.Black, 210);
            decal.MaxExpiryTimeMillis = SCORCH_MARK_EXPIRY_TIME_MILLIS;
            decals.Enqueue(decal);
        }
    }
}